package com.hb.trade.api.routine.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Service;
import com.hb.trade.api.routine.service.RsaAlgorithmService;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;


/**
 * @Description RSA 公私钥加密
 * @ClassName RsaAlgorithmServiceImpl
 * @Author LJS
 * @date 2022-4-12 17:22:18
 */
@Slf4j
@Service
public class RsaAlgorithmServiceImpl implements RsaAlgorithmService {
    private final String privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAK3wT53hTo40EKGHZK36FKC/k/6SxD65+Bj8DTs+klXwFobTrv3zy8QxryKUvLk82To0U3AxSlXPmohOqw+1zmsgPJnv+7d/yqCc7iZKttrvBt5256mynt6QB7SFALcpvdE/4NzV0Oru6mwCC0JBicNVxGG/dBen1So8hl8xQp7fAgMBAAECgYA4VmzDO1XBU3XLi9n9S9HgmwypWQo9Muj71q/Gb6bgNOpm8ytrz8pBGsKaFcjmyqGla4tW/HASSTLJUYmwhfd8SpxP0a3zPhpEhkTjE8MGNp/gJ5kCusmoHIZPeKzPBmLwW8VjG7qUBgMG182arCVXeu2EXwLRvDHa4WsS2WflMQJBAPjyB0SAy/OdQOSueLWk17jlWsIifu88LCYmlw0jv57ktx40x49zMVGg2/fz0qhg7LYoWZXVMt19XQQHg5owsncCQQCy3iW4CZqvErORNIfEGoPlpxhOHM3OancA176UVW1OmJ6my3cIYYPRtklL/qJ1cwlkrw8jlpyJwQNeBvPyHWjZAkA5cQ9APT/SPIzGt4Ne1mR4Y2Onu/HyHb5KJ0lfXMpQTZH7RE5vyTKuFSHserQ26fZ0qabSLdmIxe56iYlSQL5NAkAsb2Wdb71eyt2TMCKeRBwKPFWcailyox5HNlGhFgven6miNB+xFFCkE43n9C010ks+GQNAyxS+YBXPERkfR9EZAkAHsvM9XBIDSKMxZa5PvCm6jFSTzit0ug6EwDojwLv/+QvoTBHqnQ5AbKqtrL2Dg9jG0UxhZdoJRe9BQEAB0bfq";

    private final String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCt8E+d4U6ONBChh2St+hSgv5P+ksQ+ufgY/A07PpJV8BaG067988vEMa8ilLy5PNk6NFNwMUpVz5qITqsPtc5rIDyZ7/u3f8qgnO4mSrba7wbeduepsp7ekAe0hQC3Kb3RP+Dc1dDq7upsAgtCQYnDVcRhv3QXp9UqPIZfMUKe3wIDAQAB";

    public static final String CHARSET = "UTF-8";

    public static final String RSA_ALGORITHM = "RSA";

    private static final String PKCS1PADDING = "PKCS1Padding";

    public static final String rsa = "RSA/None/PKCS1Padding";

    @Override
    public String encryptData(String sourceData) {
        try{
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            RSAPublicKey publicKeyDefault = getPublicKey();
            cipher.init(Cipher.ENCRYPT_MODE, publicKeyDefault);
            return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, sourceData.getBytes(CHARSET), publicKeyDefault.getModulus().bitLength()));
        }catch(Exception e){
            log.error("publicEncrypt failed for meg is [{}], stack is [{}]", e.getMessage(), e );
            throw new RuntimeException("加密字符串[" + sourceData + "]时遇到异常", e);
        }
    }

    @Override
    public String decryptData(String encryptData) {
        try{
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            RSAPrivateKey privateKeyDefault = getPrivateKey();
            cipher.init(Cipher.DECRYPT_MODE, privateKeyDefault);
            return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(encryptData), privateKeyDefault.getModulus().bitLength()), CHARSET);
        }catch(Exception e){
            log.error("privateDecrypt failed for meg is [{}], stack is [{}]", e.getMessage(), e );
            throw new RuntimeException("解密字符串[" + encryptData + "]时遇到异常", e);
        }
    }

    /***
     * 获取公钥
     * @return
     */
    @Override
    public String getPubKey() {
        return this.publicKey;
    }

    @Override
    public String encryptData(String data, String pubKey) {
        try{
            byte[] publicKeyBytes = Base64.decodeBase64(pubKey);
            X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            Key publicKey = keyFactory.generatePublic(x509KeySpec);
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return Base64.encodeBase64String(cipher.doFinal(data.getBytes(CHARSET)));
        }catch(Exception e){
            log.error("encryptData failed for meg is [{}], stack is [{}]", e.getMessage(), e );
            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
        }
    }

    /***
     * RSA分割code
     * @param cipher
     * @param opmode
     * @param datas
     * @param keySize
     * @return
     */
    private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize){
        int maxBlock = 0;
        if(opmode == Cipher.DECRYPT_MODE){
            maxBlock = keySize / 8;
        }else{
            maxBlock = keySize / 8 - 11;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] buff;
        int i = 0;
        try{
            while(datas.length > offSet){
                if(datas.length-offSet > maxBlock){
                    buff = cipher.doFinal(datas, offSet, maxBlock);
                }else{
                    buff = cipher.doFinal(datas, offSet, datas.length-offSet);
                }
                out.write(buff, 0, buff.length);
                i++;
                offSet = i * maxBlock;
            }
        }catch(Exception e){
            throw new RuntimeException("加解密阀值为["+maxBlock+"]的数据时发生异常", e);
        }
        byte[] resultDatas = out.toByteArray();

        try {
            if (out != null) {
                out.close();
            }
        } catch (IOException var2) {
        }
        return resultDatas;
    }

    /***
     * 获取公钥
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public RSAPublicKey getPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
        //通过X509编码的Key指令获得公钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
        RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        return key;
    }

    /***
     * 获取公钥
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public RSAPublicKey getPublicKey(String pubKeys) throws NoSuchAlgorithmException, InvalidKeySpecException {
        //通过X509编码的Key指令获得公钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(pubKeys));
        RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        return key;
    }


    /**
     * @Title: getPrivateKey
     * @Description: 获取RSA私钥
     * @DateTime 2018年11月19日 下午7:47:03
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public RSAPrivateKey getPrivateKey() throws NoSuchAlgorithmException, InvalidKeySpecException {

        //通过PKCS#8编码的Key指令获得私钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
        RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
        return key;
    }

    public static void main(String[] args) throws Exception{
        String str = "123456y?1";
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCt8E+d4U6ONBChh2St+hSgv5P+ksQ+ufgY/A07PpJV8BaG067988vEMa8ilLy5PNk6NFNwMUpVz5qITqsPtc5rIDyZ7/u3f8qgnO4mSrba7wbeduepsp7ekAe0hQC3Kb3RP+Dc1dDq7upsAgtCQYnDVcRhv3QXp9UqPIZfMUKe3wIDAQAB"));
        RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        RSAPublicKey publicKeyDefault =key;
        cipher.init(Cipher.ENCRYPT_MODE, publicKeyDefault);
        String res =  Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, str.getBytes(CHARSET), publicKeyDefault.getModulus().bitLength()));
        System.out.println(res);
    }
}
